<?php

namespace WenRuns\Laravel\Admin\Form\Field\Grid;

use Encore\Admin\Form\Field;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Cache;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Checkbox;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Color;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Date;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\DateTime;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Decimal;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Display;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Email;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Expand;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Group;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Hidden;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Html;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Image;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Input;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Modal;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Number;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Password;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Phone;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\PowerSwitch;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Radio;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Range;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Select;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Tab;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Textarea;
use WenRuns\Laravel\Admin\Form\Field\Grid\Field\Time;
use WenRuns\Laravel\Laravel;

/**
 * @method Input input($column, $label = '')
 * @method Select select($column, $label = '')
 * @method Number number($column, $label = '')
 * @method Textarea textarea($column, $label = '')
 * @method Radio radio($column, $label = '')
 * @method Checkbox checkbox($column, $label = '')
 * @method Expand expand($column, $label, $content)
 * @method Modal modal($column, $label = '', $content = null, $title = '')
 * @method Color color($column, $label = '')
 * @method Decimal decimal($column, $label = '')
 * @method Display display($column, $label = '')
 * @method Hidden hidden($column, $label = '')
 * @method Time time($column, $label = '')
 * @method Date date($column, $label = '')
 * @method DateTime datetime($column, $label = '')
 * @method PowerSwitch switch ($column, $label = '')
 * @method Image image($column, $label = '')
 * @method Html html($label, $html)
 * @method Group group($label, \Closure $closure)
 * @method Email email($column, $label)
 * @method Phone phone($column, $label)
 * @method Password password($column, $label)
 * @method Range range($column, $label)
 */
class Grid extends Field
{
    protected $view = 'WenAdmin::form.grid';

    /**
     * 字段
     * @var array
     */
    protected $columns = [];
    /**
     * 回调
     * @var mixed|null
     */
    protected $closure;
    /**
     * 表头级数
     * @var int
     */
    public $headerRow = 1;

    /**
     * 取消分页
     * @var bool
     */
    protected $disabledPagination = false;

    /**
     * 每页记录数选项
     * @var int[]
     */
    protected $pages = [10, 20, 30, 50, 100];

    /**
     * 默认每页显示记录数量
     * @var int
     */
    protected $perPage = 20;
    /**
     * 异步请求数据api
     * @var null
     */
    protected $api = '';

    /**
     * 操作
     * @var Actions
     */
    protected $actions;

    /**
     * 父亲对象
     * @var mixed|null
     */
    protected $parent;

    /**
     * @var array
     */
    protected $events = [];

    /**
     * @var string
     */
    protected $md5String;

    /**
     * @var string
     */
    protected $keyName = 'id';

    /**
     * @var string[]
     */
    static public $methods = [
        'input' => Input::class,
        'select' => Select::class,
        'number' => Number::class,
        'textarea' => Textarea::class,
        'radio' => Radio::class,
        'checkbox' => Checkbox::class,
        'expand' => Expand::class,
        'modal' => Modal::class,
        'color' => Color::class,
        'decimal' => Decimal::class,
        'display' => Display::class,
        'hidden' => Hidden::class,
        'time' => Time::class,
        'date' => Date::class,
        'datetime' => DateTime::class,
        'switch' => PowerSwitch::class,
        'image' => Image::class,
        'html' => Html::class,
        'group' => Group::class,
        'email' => Email::class,
        'phone' => Phone::class,
        'password' => Password::class,
        'range' => Range::class,
        'tab' => Tab::class,
    ];


    public function __construct($column = '', $arguments = [], $parent = null)
    {

        $this->actions = new Actions();
        $this->parent = $parent;
        $this->closure = array_shift($arguments);
        parent::__construct($column, $arguments);
        $this->id = str_replace('.', '', $this->column() . '-' . (microtime(true) + mt_rand(0, 1000000)));
        /** @var Router $router */
        $router = app('router');
        $this->md5String = md5($router->currentRouteAction());
        if (function_exists('\Opis\Closure\unserialize')) {
            Cache::put('wenruns.grid.form.' . $this->md5String, ['closure' => \Opis\Closure\serialize($this->closure), 'column' => $this->column], 160);
        }
    }

    /**
     * @param $url
     * @return $this
     */
    public function setApi($url)
    {
        $this->api = $url;
        return $this;
    }

    /**
     * 设置主键字段
     * @param $name
     * @return $this
     */
    public function setKeyName($name)
    {
        $this->keyName = $name;
        return $this;
    }


    /**
     * @param $eventName
     * @param $jsCallback
     * @return $this
     */
    public function event($event, $jsEventCallback)
    {
        if (is_callable($jsEventCallback)) {
            $jsEventCallback = call_user_func($jsEventCallback);
        }
        $this->events[$event][] = compressHtml($jsEventCallback);
        return $this;
    }

    /**
     * @param \Closure $closure
     * @return $this
     */
    public function actions(\Closure $closure)
    {
        call_user_func($closure, $this->actions);
        return $this;
    }

    /**
     * @param $disabled
     * @return $this
     */
    public function disableActions($disabled = true)
    {
        $this->actions->disabled($disabled);
        return $this;
    }

    /**
     * 取消分页
     * @param $value
     * @return $this
     */
    public function disablePagination($value = true)
    {
        $this->disabledPagination = $value;
        return $this;
    }

    /**
     * 设置默认每页记录条数
     * @param $number
     * @return $this
     */
    public function perPage($number)
    {
        $this->perPage = $number;
        return $this;
    }

    /**
     * 设置每页记录条数选项
     * @param $pages
     * @return $this
     */
    public function perPages($pages)
    {
        $this->pages = (array)$pages;
        return $this;
    }

    /**
     * 输入出
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
     */
    public function render()
    {
        call_user_func($this->closure, $this);
        Laravel::loadJs('form/grid.js');
        Laravel::loadCss('form/grid.css');
        $opt = $this->initOption();
        $this->addVariables([
            '__content__' => $this->content($opt),
        ]);
        $this->setScript($this->initScript($opt));
        return parent::render(); // TODO: Change the autogenerated stub
    }

    /**
     * script代码实现
     * @param $opt
     * @return string
     */
    public function initScript($opt = []): string
    {
        $optStr = json_encode($opt, JSON_UNESCAPED_UNICODE);
        $script = Laravel::makeAssetLoadUrl('form/grid.js');
        $css = Laravel::makeAssetLoadUrl('form/grid.css');
        return <<<SCRIPT
if(typeof FormGrid == "undefined"){
    let script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "{$script}";
    script.onload = ()=>{
        window.formGrid = new FormGrid("{$this->id}", {$optStr});
    }
    let css = document.createElement("link");
    css.rel = "stylesheet";
    css.href = "{$css}";
    document.head.append(script);
    document.head.append(css);
}else{
    window.formGrid = new FormGrid("{$this->id}", {$optStr});
}

SCRIPT;
    }

    protected function content($opt)
    {
//        dd($this->columns);
        list($header, $colgroup) = $this->header($opt, $this->columns);
        $pagination = $this->pagination();
        return <<<HTML
<div class="form-grid" id="{$this->id}">
    <div class="wen-form-grid" data-id="{$this->id}">
        <table class="form-grid-table" data-id="{$this->id}">
            <colgroup>{$colgroup}</colgroup>
            <thead class="form-grid-header" data-id="{$this->id}">{$header}</thead>
            <tbody class="form-grid-body" data-id="{$this->id}">
            <tr class="form-grid-empty" data-id="{$this->id}">
                <td colspan="100%" >
                    <svg t="1562312016538" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2076" width="128" height="128" style="fill: #e9e9e9;">
                        <path d="M512.8 198.5c12.2 0 22-9.8 22-22v-90c0-12.2-9.8-22-22-22s-22 9.8-22 22v90c0 12.2 9.9 22 22 22zM307 247.8c8.6 8.6 22.5 8.6 31.1 0 8.6-8.6 8.6-22.5 0-31.1L274.5 153c-8.6-8.6-22.5-8.6-31.1 0-8.6 8.6-8.6 22.5 0 31.1l63.6 63.7zM683.9 247.8c8.6 8.6 22.5 8.6 31.1 0l63.6-63.6c8.6-8.6 8.6-22.5 0-31.1-8.6-8.6-22.5-8.6-31.1 0l-63.6 63.6c-8.6 8.6-8.6 22.5 0 31.1zM927 679.9l-53.9-234.2c-2.8-9.9-4.9-20-6.9-30.1-3.7-18.2-19.9-31.9-39.2-31.9H197c-19.9 0-36.4 14.5-39.5 33.5-1 6.3-2.2 12.5-3.9 18.7L97 679.9v239.6c0 22.1 17.9 40 40 40h750c22.1 0 40-17.9 40-40V679.9z m-315-40c0 55.2-44.8 100-100 100s-100-44.8-100-100H149.6l42.5-193.3c2.4-8.5 3.8-16.7 4.8-22.9h630c2.2 11 4.5 21.8 7.6 32.7l39.8 183.5H612z" p-id="2077"></path>
                    </svg>
                </td>
            </tr>
            <tr class="form-grid-loading-tr" data-id="{$this->id}">
                <td colspan="100%" class="form-grid-loading-td" >
                    <div class="form-grid-loading">
                        <i class="fa fa-spinner"></i>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
    {$pagination}
    <div class="form-grid-hidden" data-id="{$this->id}"></div>
</div>
HTML;
    }

    /**
     * 分页
     * @return string
     */
    public function pagination()
    {
        if ($this->disabledPagination) {
            return '';
        }
        $optionsStr = '';
        foreach ($this->pages as $page) {
            $optionsStr .= '<option value="' . $page . '" ' . ($page == $this->perPage ? 'selected="selected"' : '') . '>' . $page . '</option>';
        }
        return <<<THML
<div class="form-grid-pagination box-footer clearfix" data-id="{$this->id}">
    <text>总共 </text>
    <b id="total">0</b>
    <text> 条，</text>
    <ul class="pagination pagination-sm no-margin pull-right">
        <li class="page-item disabled"><span class="page-link">«</span></li>
        <li class="page-item active" data-page="1"><span class="page-link">1</span></li>
        <li class="page-item"><span class="page-link">»</span></li>
    </ul>
    <small>显示&nbsp;</small>
    <select class="input-sm grid-per-pager" name="per-page">
        {$optionsStr}
    </select>
    <small>&nbsp;条</small>
</div>
THML;

    }

    /**
     * 表头
     * @param $opt
     * @param $columns
     * @param $row
     * @param $colgroup
     * @return array
     */
    public function header($opt, $columns, $row = 1, &$colgroup = '')
    {
        $header = '<tr>';
        $nextTr = '';
        foreach ($columns as $key => $column) {
            /** @var Column $column */
            $text = empty($column->getLabel() ?? null) ? $column->getName() : $column->getLabel();
            switch ($column->getType()) {
                case 'hidden':
                    break;
                case 'group':
                    $header .= '<th class="column-group" colspan="' . $column->colspan . '"><text>' . $text . '</text>' . $this->checkFilter($column) . $this->checkSort($column) . $this->checkHelpText($column) . '</th>';
                    list($nextTr) = $this->header($opt, $column->columns, $column->level, $colgroup);
                    break;
                default:
                    $rowspan = ($opt['headCol'] ?? 1) - $row + 1;
                    $header .= '<th class="column-' . $column->getType() . ' column-' . ($column->getName() ?? '') . '" rowspan="' . $rowspan . '"><text>' . $text . '</text>' . $this->checkFilter($column) . $this->checkSort($column) . $this->checkHelpText($column) . '</th>';
                    // todo:: 属性调整
                    $styleString = '';
                    if ($style = $column->attributes['style'] ?? null) {
                        $styleString = 'style="';
                        if (is_array($style)) {
                            foreach ($style as $name => $value) {
                                $styleString .= $name . ':' . $value . ';';
                            }
                        } else {
                            $styleString .= $style;
                        }
                        $styleString .= '"';
                    }
                    $colgroup .= '<col ' . $styleString . '/>';

            }
        }
        if ($row == 1) {
            $header .= $this->checkActions($opt);
        }
        $header .= '</tr>' . $nextTr;
        return [$header, $colgroup];
    }

    /**
     * 操作检测
     * @param $opt
     * @return string
     */
    protected function checkActions($opt)
    {
        if ($this->actions->disabled) {
            return '';
        }
        $createButton = '';
        if (!$this->actions->disableCreateButton) {
            $createButton = '<i class="fa fa-plus-square actions-create"></i>';
        }
        $rowspan = $opt['headCol'] ?? 1;
        return <<<HTML
<th class="column-actions" rowspan="{$rowspan}">
    <span>操作</span>
    {$createButton}
</th>
HTML;
    }

    /**
     * 帮助文本检测
     * @param Column $column
     * @return string
     */
    protected function checkHelpText(Column $column)
    {
        if ($helpText = $column->getConfigs('help')) {
            return '<i class="fa fa-question-circle form-grid-helper" data-text="' . $helpText . '"></i>';
        }
        return '';
    }

    /**
     * 筛选检测
     * @param Column $column
     * @return string
     */
    protected function checkFilter(Column $column)
    {
        $filter = '';
        $configs = $column->getConfigs();
        if ($configs['filter'] ?? null) {
            $filter = '<a href="javascript:void(0);" class="dropdown-toggle form-grid-filter" data-toggle="dropdown"><i class="fa fa-filter"></i></a>';
        }
        return $filter;
    }

    /**
     * 排序检测
     * @param Column $column
     * @return string
     */
    protected function checkSort(Column $column)
    {
        $sort = '';
        $configs = $column->getConfigs();
        if ($configs['sortable'] ?? null) {
            $sort = '<a href="javascript:void(0);" class="fa fa-fw fa-sort form-grid-sorter"></a>';
        }
        return $sort;
    }


    /**
     * @param $just
     * @return array|mixed
     */
    public function initOption($just = null)
    {
        $columns = [];
        $values = $this->value();
        if (!empty($values) && !isset($values[0])) {
            $values = [$values];
        }
        foreach ($this->columns as $column) {
            /** @var Column $column */
            if ((empty($just) || $just == 2) && !empty($values)) {
                if (is_callable($column->withClosure) || $column instanceof Group || $column instanceof Expand || $column instanceof Modal) {
                    foreach ($values as $k => &$row) {
                        $row = $column->setRow($row)->getRow();
                    }
                }
            }
            if (empty($just) || $just == 1) {
                $columns[] = $column->render();
            }
        }
        return $just == 1 ? $columns : ($just == 2 ? $values : [
            'columns' => $columns,
            'data' => $values,
            'headCol' => $this->headerRow,
            'name' => $this->column(),
            'pages' => $this->pages,
            'count' => $this->perPage,
            'api' => $this->api,
            'md5String' => $this->md5String,
            'uploadUrl' => route('wenruns.laravel.service.form.grid.upload'),
            'token' => csrf_token(),
            'actions' => $this->actions->render(),
            'events' => $this->events,
            'disabledPagination' => $this->disabledPagination,
            'asyncRoute' => route('wenruns.laravel.service.form.grid'),
            'keyName' => $this->keyName,
        ]);
    }


    /**
     * 字段
     * @param $method
     * @param $parameters
     * @return Column
     * @throws \Exception
     */
    public function __call($method, $parameters)
    {
        if ($method == 'tab') {
            throw new \Exception('Method [tab] is not allowed!');
        }
        return self::checkColumn($method, $parameters, function ($column, $parameters) use ($method) {
            if ($method == 'group') {
                $parameters[] = 2;
            }
            $instance = new $column($this, ...$parameters);
            $this->columns[] = $instance;
            return $instance;
        });
    }

    /**
     * @param $name
     * @return null
     */
    public function __get($name)
    {
        return $this->$name ?? null;
    }


    public static function checkColumn($name, $parameters, \Closure $closure)
    {
        if (isset(self::$methods[$name])) {
            return call_user_func($closure, self::$methods[$name], $parameters);
        }
        throw new \Exception('method 【' . $name . '】 not exist.');
    }

//    /**
//     * 压缩html
//     * @param $string
//     * @return string
//     */
//    public static function compressHtml($string)
//    {
//        return ltrim(rtrim(preg_replace(array("/> *([^ ]*) *</", "//", "'/\*[^*]*\*/'", "/\r\n/", "/\n/", "/\t/", '/>[ ]+</', '/`/'),
//            array(">\\1<", '', '', '', '', '', '><', '\`'), $string)));
//    }


}
